package org.alive.ee.app.repository;

import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.CallableStatementCallback;
import org.springframework.jdbc.core.CallableStatementCreator;
import org.springframework.jdbc.core.ConnectionCallback;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCallback;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.StatementCallback;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.jdbc.support.rowset.SqlRowSet;
import org.springframework.stereotype.Repository;

@Repository
public class BaseDAO {

	@PersistenceContext
	protected EntityManager em;

	@Autowired
	protected JdbcTemplate jdbcTemplate;

	protected Logger logger = LoggerFactory.getLogger(this.getClass());

	/** Batch size for batch insert or update, set by configuration */
	private int batchSize = 50;

	// -- CRUD methods and simple query that uses EntityManager

	public <E> void save(E entity) {
		em.persist(entity);
	}

	public <E> void batchSave(List<E> list) {
		for (int i = 0; i < list.size(); i++) {
			em.persist(list.get(i));
			if (i % batchSize == 0) {
				em.flush();
				em.clear();
			}
		}
	}

	public <E> boolean update(E entity) {
		em.merge(entity);
		return true;
	}

	public <E> void batchUpdate(List<E> list) {
		for (int i = 0; i < list.size(); i++) {
			em.merge(list.get(i));
			if (i % batchSize == 0) {
				em.flush();
				em.clear();
			}
		}
	}

	public <E> boolean delete(E entity) {
		em.remove(entity);
		return true;
	}

	public <E, K> E find(Class<E> clz, K id) {
		return em.find(clz, id);
	}

	@SuppressWarnings("unchecked")
	public <E> List<E> query(Class<E> clz) {
		Query query = em.createQuery(" from " + clz.getSimpleName());
		return query.getResultList();
	}

	// -- Methods that uses JdbcTemplate.

	protected int[] batchUpdate(String sql, BatchPreparedStatementSetter pss) {
		return jdbcTemplate.batchUpdate(sql, pss);
	}

	protected int[] batchUpdate(String[] sqls) {
		return jdbcTemplate.batchUpdate(sqls);
	}

	protected Map<String, Object> call(CallableStatementCreator csc, final List<SqlParameter> declaredParameters) {
		return jdbcTemplate.call(csc, declaredParameters);
	}

	protected <T> T execute(CallableStatementCreator csc, CallableStatementCallback<T> cscb) {
		return jdbcTemplate.execute(csc, cscb);
	}

	protected <T> T execute(ConnectionCallback<T> ccb) {
		return jdbcTemplate.execute(ccb);
	}

	protected <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> pscb) {
		return jdbcTemplate.execute(psc, pscb);
	}

	protected <T> T execute(StatementCallback<T> scb) {
		return jdbcTemplate.execute(scb);
	}

	protected void execute(String sql) {
		jdbcTemplate.execute(sql);
	}

	protected <T> T execute(String sql, CallableStatementCallback<T> cscb) {
		return jdbcTemplate.execute(sql, cscb);
	}

	protected <T> T execute(String sql, PreparedStatementCallback<T> pscb) {
		return jdbcTemplate.execute(sql, pscb);
	}

	protected <T> T query(PreparedStatementCreator psc, ResultSetExtractor<T> rse) {
		return jdbcTemplate.query(psc, rse);
	}

	protected <T> List<T> query(PreparedStatementCreator psc, RowMapper<T> rowMapper) {
		return jdbcTemplate.query(psc, rowMapper);
	}

	protected <T> T query(String sql, Object[] args, int[] argTypes, ResultSetExtractor<T> rse) {
		return jdbcTemplate.query(sql, args, argTypes, rse);
	}

	protected <T> List<T> query(String sql, Object[] args, int[] argTypes, RowMapper<T> rowMapper) {
		return jdbcTemplate.query(sql, args, argTypes, rowMapper);
	}

	protected <T> T query(String sql, Object[] args, ResultSetExtractor<T> rse) {
		return jdbcTemplate.query(sql, args, rse);
	}

	protected <T> List<T> query(String sql, Object[] args, RowMapper<T> rowMapper) {
		return jdbcTemplate.query(sql, args, rowMapper);
	}

	protected <T> T query(String sql, PreparedStatementSetter pss, ResultSetExtractor<T> rse) {
		return jdbcTemplate.query(sql, pss, rse);
	}

	protected <T> List<T> query(String sql, PreparedStatementSetter pss, RowMapper<T> rowMapper) {
		return jdbcTemplate.query(sql, pss, rowMapper);
	}

	protected <T> T query(String sql, ResultSetExtractor<T> rse) {
		return jdbcTemplate.query(sql, rse);
	}

	protected <T> List<T> query(String sql, RowMapper<T> rowMapper) {
		return jdbcTemplate.query(sql, rowMapper);
	}

	protected int queryForInt(String sql) {
		return jdbcTemplate.queryForInt(sql);
	}

	protected int queryForInt(String sql, Object[] args) {
		return jdbcTemplate.queryForInt(sql, args);
	}

	protected int queryForInt(String sql, Object[] args, int[] argTypes) {
		return jdbcTemplate.queryForInt(sql, args, argTypes);
	}

	protected List<Map<String, Object>> queryForList(String sql) {
		return jdbcTemplate.queryForList(sql);
	}

	protected <T> List<T> queryForList(String sql, Class<T> elementType) {
		return jdbcTemplate.queryForList(sql, elementType);
	}

	protected List<Map<String, Object>> queryForList(String sql, Object[] args) {
		return jdbcTemplate.queryForList(sql, args);
	}

	protected <T> List<T> queryForList(String sql, Object[] args, Class<T> elementType) {
		return jdbcTemplate.queryForList(sql, args, elementType);
	}

	protected List<Map<String, Object>> queryForList(String sql, Object[] args, int[] argTypes) {
		return jdbcTemplate.queryForList(sql, args, argTypes);
	}

	protected <T> List<T> queryForList(String sql, Object[] args, int[] argTypes, Class<T> elementType) {
		return jdbcTemplate.queryForList(sql, args, argTypes, elementType);
	}

	protected long queryForLong(String sql) {
		return jdbcTemplate.queryForLong(sql);
	}

	protected long queryForLong(String sql, Object[] args) {
		return jdbcTemplate.queryForLong(sql, args);
	}

	protected long queryForLong(String sql, Object[] args, int[] argTypes) {
		return jdbcTemplate.queryForLong(sql, args, argTypes);
	}

	protected Map<String, Object> queryForMap(String sql) {
		return jdbcTemplate.queryForMap(sql);
	}

	protected Map<String, Object> queryForMap(String sql, Object[] args) {
		return jdbcTemplate.queryForMap(sql, args);
	}

	protected Map<String, Object> queryForMap(String sql, Object[] args, int[] argTypes) {
		return jdbcTemplate.queryForMap(sql, args, argTypes);
	}

	protected <T> T queryForObject(String sql, Class<T> elementType) {
		return jdbcTemplate.queryForObject(sql, elementType);
	}

	protected <T> T queryForObject(String sql, Object[] args, Class<T> elementType) {
		return jdbcTemplate.queryForObject(sql, args, elementType);
	}

	protected <T> T queryForObject(String sql, Object[] args, int[] argTypes, Class<T> elementType) {
		return jdbcTemplate.queryForObject(sql, args, argTypes, elementType);
	}

	protected <T> T queryForObject(String sql, Object[] args, int[] argTypes, RowMapper<T> rowMapper) {
		return jdbcTemplate.queryForObject(sql, args, argTypes, rowMapper);
	}

	protected <T> T queryForObject(String sql, Object[] args, RowMapper<T> rowMapper) {
		return jdbcTemplate.queryForObject(sql, args, rowMapper);
	}

	public <T> T queryForObject(String sql, RowMapper<T> rowMapper) throws DataAccessException {
		return jdbcTemplate.queryForObject(sql, rowMapper);
	}

	protected SqlRowSet queryForRowSet(String sql) {
		return jdbcTemplate.queryForRowSet(sql);
	}

	protected SqlRowSet queryForRowSet(String sql, Object[] args) {
		return jdbcTemplate.queryForRowSet(sql, args);
	}

	protected SqlRowSet queryForRowSet(String sql, Object[] args, int[] argTypes) {
		return jdbcTemplate.queryForRowSet(sql, args, argTypes);
	}

	protected int update(PreparedStatementCreator psc) {
		return jdbcTemplate.update(psc);
	}

	protected int update(PreparedStatementCreator psc, KeyHolder keyHolder) {
		return jdbcTemplate.update(psc, keyHolder);
	}

	protected int update(String sql) {
		return jdbcTemplate.update(sql);
	}

	protected int update(String sql, Object[] args) {
		return jdbcTemplate.update(sql, args);
	}

	protected int update(String sql, Object[] args, int[] argTypes) {
		return jdbcTemplate.update(sql, args, argTypes);
	}

	protected int update(String sql, PreparedStatementSetter pss) {
		return jdbcTemplate.update(sql, pss);
	}
}
